package edu.usc.csci578.manager;

import java.util.*;

import edu.usc.csci578.exceptions.ClusterDontExistException;
import edu.usc.csci578.model.*;
import edu.usc.csci578.util.ModelParser;

public class ModelManager {
	private static ModelManager mm;

	public static ModelManager getInstance() {
		if (mm == null) {
			mm = new ModelManager();
		}
		return mm;
	}

	private Map<String, Cluster> clusterMap = null;
	private Map<String, SClass> classMap = null;
	private Map<Integer, STopic> topicMap = null;
	private Map<String, SMethod> methodMap = null;

	private boolean allowEdit = true;

	private ModelManager() {
		clear();
	}

	public void clear() {
		clusterMap = new HashMap<String, Cluster>();
		classMap = new HashMap<String, SClass>();
		topicMap = new HashMap<Integer, STopic>();
		methodMap = new HashMap<String, SMethod>();
		allowEdit = true;
		add(new TBoolean());
		add(new TDouble());
		add(new TFloat());
		add(new TInteger());
		add(new TLong());
		add(new TVoid());
	}

	private boolean add(Cluster cluster) {
		if (cluster != null && !clusterMap.containsKey(cluster.getName())) {
			clusterMap.put(cluster.getName(), cluster);
			return true;
		}
		return false;
	}

	private boolean add(SClass aClass) {
		if (aClass != null && !classMap.containsKey(aClass.getName())) {
			classMap.put(aClass.getName(), aClass);
			return true;
		}
		return false;
	}

	public boolean add(STopic topic) {
		if (topic != null && !topicMap.containsKey(topic.getId())) {
			topicMap.put(topic.getId(), topic);
			return true;
		}
		return false;
	}

	private boolean add(SMethod method) {
		if (method != null && !methodMap.containsKey(method.getName())) {
			methodMap.put(method.getUniqueName(), method);
			return true;
		}
		return false;
	}

	public Cluster getCluster(String name, boolean editable) throws Exception {
		if (name == null) {
			throw new Exception("Shit XML");
		}
		Cluster ret = clusterMap.get(name);

		if (ret == null) {
			if (editable && allowEdit) {
				ret = new Cluster(name);
				add(ret);
			} else {
				throw new ClusterDontExistException(
						"Shit XML: Cluster not exist");
			}
		}
		return ret;
	}

	public SClass getClass(String name, boolean editable) throws Exception {
		if (name == null) {
			throw new Exception("Shit XML: name == null");
		}
		SClass ret = classMap.get(name);

		if (ret == null) {
			if (editable && allowEdit) {
				String[] names = ModelParser.parseClassName(name);
				ret = new SClass(names[0], names[1]);
				add(ret);
			} else {
				String[] names = ModelParser.parseClassName(name);
				System.err.println(names[0]);
				ret = classMap.get(names[0]);
				if (ret == null)
					throw new Exception("Shit XML: Class not exist");
			}
		}
		return ret;
	}

	public STopic getTopic(Integer id, boolean editable) throws Exception {
		if (id < 0) {
			throw new Exception("Shit XML: id < 0");
		}
		STopic ret = topicMap.get(id);
		if (ret == null) {
			if (editable && allowEdit) {
				ret = new STopic(id);
				topicMap.put(id, ret);
			} else
				throw new Exception("Shit XML: Topic not exist");
		}
		return ret;
	}

	public SMethod getMethod(String name, boolean editable) throws Exception {
		if (name == null) {
			throw new Exception("Shit XML: name == null");
		}
		SMethod ret = methodMap.get(name);
		if (ret == null) {
			if (editable && allowEdit) {
				String[] names = ModelParser.parseMethodName(name);
				String[] classNames = ModelParser.parseClassName(names[1]);
				SClass owner = getClass(classNames[0], editable);
				owner.setPackageName(classNames[1]);
				ret = new SMethod(names[0],
						ModelParser.getMethodType(names[2]), owner);
				add(ret);
			} else {
				String[] names = ModelParser.parseMethodName(name);
				// System.out.println(names[1] + "." + names[0]);
				ret = methodMap.get(names[1] + "." + names[0]);
				if (ret == null)
					throw new Exception("Shit XML: Method not exist");
			}
		}
		return ret;
	}

	public ClusterEdge addClusterEdge(String src, String tgt, boolean editable)
			throws Exception {
		Cluster srcCluster = getCluster(src, editable);
		Cluster tgtCluster = getCluster(tgt, editable);
		ClusterEdge ce = new ClusterEdge(srcCluster, tgtCluster);
		return ce;
	}

	public int countClusters() {
		return clusterMap.keySet().size();
	}

	public int countClasses() {
		return classMap.keySet().size();
	}

	public int countTopics() {
		return topicMap.keySet().size();
	}

	public int countMethods() {
		return methodMap.keySet().size();
	}

	public int countEdges() {
		int i = 0;
		for (Cluster c : clusterMap.values()) {
			i += c.getEdgesIn().size();
		}
		return i;
	}

	public boolean containsCluster(String name) {
		return clusterMap.containsKey(name);
	}

	public boolean containsCluster(Cluster cluster) {
		return clusterMap.containsValue(cluster);
	}

	public boolean containsClass(String name) {
		return classMap.containsKey(name);
	}

	public boolean containsClass(SClass aClass) {
		return classMap.containsValue(aClass);
	}

	public boolean containsTopic(String name) {
		return topicMap.containsKey(name);
	}

	public boolean containsTopic(STopic topic) {
		return topicMap.containsValue(topic);
	}

	public boolean containsMethod(String name) {
		return methodMap.containsKey(name);
	}

	public boolean containsMethod(SMethod method) {
		return methodMap.containsValue(method);
	}

	public List<Cluster> getAllClusters() {
		return new ArrayList<Cluster>(clusterMap.values());
	}

	public List<SClass> getAllClasses() {
		return new ArrayList<SClass>(classMap.values());
	}

	public List<STopic> getAllTopics() {
		return new ArrayList<STopic>(topicMap.values());
	}

	public List<SMethod> getAllMethods() {
		return new ArrayList<SMethod>(methodMap.values());
	}

	public Set<ClusterEdge> getAllEdges() {
		Set<ClusterEdge> ceSet = new HashSet<ClusterEdge>();
		for (Cluster c : getAllClusters()) {
			ceSet.addAll(c.getEdgesAll());
		}
		return ceSet;
	}

	public void turnOff() {
		allowEdit = false;
	}

	public String toString() {
		String temp = "";
		for (Cluster cluster : clusterMap.values()) {
			temp += "\n" + cluster.toString();
		}
		for (SClass aClass : classMap.values()) {
			temp += "\n" + aClass.toString();
		}
		for (STopic topic : topicMap.values()) {
			temp += "\n" + topic.toString();
		}
		for (SMethod method : methodMap.values()) {
			temp += "\n" + method.getUniqueName();
		}
		return temp;
	}
}
